#include "libauth.h"
#include "transferdata.pb.h"
#include <time.h>
using namespace MPU;
#ifdef _DEBUG
#pragma comment(lib, "..\\..\\lib\\debug\\libyate.lib")
#pragma comment(lib, "..\\..\\lib\\debug\\framework.lib")
#pragma comment(lib, "..\\..\\..\\Develop\\SDK\\protobuf\\vsprojects\\Debug\\libprotobuf.lib")
#else
#pragma comment(lib, "..\\..\\lib\\release\\libyate.lib")
#pragma comment(lib, "..\\..\\lib\\release\\framework.lib")
#pragma comment(lib, "..\\..\\..\\Develop\\SDK\\protobuf\\vsprojects\\Release\\libprotobuf.lib")
#endif
#pragma comment(lib, "ws2_32.lib")

static char* getTime();
static bool AnsiToUtf8(char* _dir, int _len, const char* _src);

AuthModule::AuthModule() :
	Module("Auth", "misc") {
		m_authThread = NULL;
}

AuthModule::~AuthModule() {
	Output("Unload module Auth");
}

void AuthModule::initialize() {
	Output("Initializing module Auth");
	Engine::install(new MessageRelay("engine.start", this, Private, 150));
	installRelay(Private + 1, "engine.stop");
}

bool AuthModule::received(Message& msg, int id) {
	if (msg == "engine.start") {
		startAuth();
	} else if (msg == "engine.stop") {
		stopAuth();
		Output("stop");
	}
	return Module::received(msg, id);
}

void AuthModule::startAuth()
{
	if (m_authThread == NULL)
	{
		m_authThread = new AuthThread();
		m_authThread->startup();
	}
}

void AuthModule::stopAuth()
{
	if (m_authThread != NULL)
		m_authThread->exit();
	m_authThread = NULL;
}


INIT_PLUGIN(AuthModule);

AuthThread::AuthThread()
{
	m_exit = false;
}

AuthThread::~AuthThread()
{

}

void AuthThread::run()
{
	SOCKET ListenSocket;
	ListenSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
	if ( ListenSocket == INVALID_SOCKET )
	{
		Output("socket failed with error");
		return;
	}
	sockaddr_in service;
	service.sin_family	= AF_INET;
	service.sin_addr.s_addr = inet_addr( "0.0.0.0" );
	service.sin_port	= htons( 0xadd );
	if ( bind( ListenSocket, (SOCKADDR *) &service, sizeof(service) ) == SOCKET_ERROR )
	{
		Output("bind failed with error");
		closesocket( ListenSocket );
		return;
	}
	if ( listen( ListenSocket, 1 ) == SOCKET_ERROR )
	{
		Output( "listen failed with error\n");
		closesocket( ListenSocket );
		return;
	}
	SOCKET AcceptSocket;
	Output( "Waiting for client to connect..." );
	char	recvbuf[1024];
	int	recvbuflen = sizeof(recvbuf);
	while (!m_exit)
	{
		AcceptSocket = accept( ListenSocket, NULL, NULL );
		if ( AcceptSocket == INVALID_SOCKET )
		{
			Output( "accept failed with error");
			break;
		}else
			Output( "Client connected." );

		int process_size = 0;
		for (;; )
		{
			int iResult = recv( AcceptSocket, recvbuf + process_size, recvbuflen - process_size, 0 );
			if ( iResult > 0 )
			{
				Output( "Bytes received: %d\n", iResult );

				android and;
				memset(&and, 0, sizeof(and));
				process_size += process_data(and, recvbuf, iResult);
				response(AcceptSocket, and);
				break;
			}
			else if ( iResult == 0 )
			{
				Output( "Connection closed" );
				break;
			}
			else 
			{
				Output( "recv failed");
				break;
			}
		}
		closesocket( AcceptSocket );
	}
	closesocket( ListenSocket );
}

void AuthThread::cleanup()
{
	delete this;
}

void AuthThread::exit()
{
	m_exit = true;
}

void AuthThread::response(SOCKET sock, android& and)
{
	Message m("sqlite");
	m.addParam("sql_id", "5");
	license lic;
	memset(&lic, 0, sizeof(lic));
	strcpy(lic.uuid, and.uuid);
	ParamAddressEx param(&lic);
	m.userData((RefObject*)param.getObject("RefObject"));
	Engine::dispatch(m);

	ParamAddress *addr = (ParamAddress *)m.userData()->getObject("ParamAddress");
	int row = m.getIntValue("rows", 0);
	license *ll = (license *)addr->getParam();

	License license;
	license.set_uuid(ll->uuid);
	license.set_create_time(ll->create_time);
	license.set_expire_time(ll->expire_time);
	license.set_type(ll->type);
	license.set_permission(ll->permission);
	license.set_description(ll->description);
	char buffer[1024];
	license.SerializeToArray(buffer + sizeof(int), sizeof(buffer)-sizeof(int));
	int *size = (int*) buffer;
	*size = license.ByteSize();
	send(sock, buffer, sizeof(int) + license.ByteSize(), 0);
}

int AuthThread::process_data(android& and, char *data, int size)
{
	int *len = (int*) data;
	if (*len <= (size-sizeof(int)))
	{
		Android andProtobuf;
		bool ret = andProtobuf.ParseFromArray(data + sizeof(int), *len);
		if (ret)
		{
			memset(&and, 0, sizeof(and));
			strcpy(and.uuid, andProtobuf.uuid().c_str());
			strcpy(and.build_brand, andProtobuf.build_brand().c_str());
			strcpy(and.build_model, andProtobuf.build_model().c_str());
			and.build_sdk_int = andProtobuf.build_sdk_int();
			strcpy(and.build_board, andProtobuf.build_board().c_str());
			strcpy(and.build_fingerprint, andProtobuf.build_fingerprint().c_str());
			strcpy(and.build_hardware, andProtobuf.build_hardware().c_str());
			strcpy(and.build_radio, andProtobuf.build_radio().c_str());
			strcpy(and.build_time, andProtobuf.build_time().c_str());
			strcpy(and.wifi_mac_address, andProtobuf.wifi_mac_address().c_str());
			strcpy(and.android_id, andProtobuf.android_id().c_str());
			strcpy(and.device_id, andProtobuf.device_id().c_str());
			and.width_pixels = andProtobuf.width_pixels();
			and.height_pixels = andProtobuf.height_pixels();
			and.density_dpi = andProtobuf.density_dpi();
			strcpy(and.register_time, andProtobuf.register_time().c_str());
			strcpy(and.last_use_time, andProtobuf.last_use_time().c_str());
			return 0;
		}
	}
	return size;
}

char* getTime()
{
	static char buf[64] = { 0 };
	time_t t = time(NULL);
	struct tm* current_time = localtime(&t);
	sprintf(buf, "%d-%02d-%02d %02d:%02d:%02d", current_time->tm_year + 1900,
		current_time->tm_mon + 1, current_time->tm_mday,
		current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
	return buf;
}

static bool AnsiToUtf8(char* _dir, int _len, const char* _src)
{
	int len = MultiByteToWideChar(CP_ACP, 0, _src, -1, 0, 0);
	wchar_t* wchar = new wchar_t[len+1];
	if (NULL == wchar) {
		return false;
	}

	MultiByteToWideChar(CP_ACP, 0, _src, -1, wchar, len);
	WideCharToMultiByte(CP_UTF8, 0, wchar, -1, _dir, _len, NULL, NULL);
	delete wchar;
	return true;
}

ParamAddressEx::ParamAddressEx(void* address) : m_address(address)
{

}

ParamAddressEx::~ParamAddressEx()
{
	m_address = NULL;
}

void* ParamAddressEx::getParam()
{
	return m_address;
}

char* ParamAddressEx::getDescription()
{
	return "";
}

void* ParamAddressEx::getObject(const String& name) const
{
	if (name == YATOM("ParamAddressEx"))
		return const_cast<ParamAddressEx*>(this);
	return ParamAddress::getObject(name);
}